/*
 * traverse.c - EDB Tree Traversal
 * Clunix, cezanne@clunix.com, 2005.3
 *
 * Copyright (c) 2001-2005 by Clunix Inc. All Rights Reserved.
 */

#include "config.h"

#include "edbd.h"
#include "edbd_conn.h"
#include "reactor.h"
#include "bdexpr.h"

typedef struct {
	edb_err_t	err;
	edbd_conn_t	edbd_conn;
	const sr_t	*sr_edexpr;
} travctx_t;

static ECM_INLINE ECMBOOL
travcheck(const sr_t *sr, ecm_ptr_t ctx, ECMBOOL is_last)
{
	travctx_t	*ptravctx = (travctx_t *)ctx;

	if (!tree_is_valid(sr))
		return ECM_FALSE;

	if (!is_last || ptravctx->sr_edexpr == NULL)
		return ECM_TRUE;

	return calc_edexpr(ptravctx->edbd_conn, sr, ptravctx->sr_edexpr);
}

edb_err_t
traverse_tree(edbd_conn_t edbd_conn, const sr_t *sr_LNP, const char *edexpr,
	      sr_t **psr_res, sr_travopt_t opt)
{
	sr_t		*sr_dir, *sr_cwd, *sr_edexpr = NULL;
	sr_t		*sr_res;
	travctx_t	travctx;
	edb_err_t	err;

	if (!ecm_safe_str(edexpr)) {
		sr_edexpr = edl_get_edexpr(edexpr, NULL, NULL);
		if (sr_edexpr == NULL)
			return -ERR_EDB_WRONGEDEXPR;
	}
	tree_lock(sr_edbroot, RECUR_SH_LOCK);

	sr_cwd = edbd_conn_getcwd(edbd_conn);
	if (sr_cwd == NULL) {
		if (!is_ABS_sr_LNP(sr_LNP)) {
			tree_unlock(sr_edbroot, RECUR_SH_LOCK);
			return -ERR_EDB_NOTEXIST;
		}
		sr_dir = sr_edbroot;
	}
	else
		sr_dir = sr_cwd;
	travctx.err = EDB_OK;
	travctx.sr_edexpr = sr_edexpr;
	travctx.edbd_conn = edbd_conn;

	sr_res = traverse_sr(sr_dir, sr_LNP, travcheck, &travctx, opt);
	tree_unlock(sr_cwd, PIN_LOCK);
	if (sr_res) {
		sr_t	*sr_ptr;

		*psr_res = sr_create_HMS(ST_PTR);
		SR_FOREACH_CHILD(sr_res, NULL, sr_ptr)
			if (tree_lock(sr_get_ptr(sr_ptr), PIN_LOCK))
				SR_APPEND_ASSERT(*psr_res, sr_dup(sr_ptr));
		sr_free(sr_res);
		err = EDB_OK;
	}
	else {
		err = travctx.err;
		if (travctx.err == EDB_OK)
			err = -ERR_EDB_NOTEXIST;
	}

	tree_unlock(sr_edbroot, RECUR_SH_LOCK);

	return err;
}
